package com.iaic.searchs;

import java.util.List;

import aima.search.framework.Node;
import aima.search.framework.Problem;
import aima.search.framework.SearchUtils;
import aima.search.informed.HillClimbingSearch;

public class MaximumSlopeClimbingSearch extends HillClimbingSearch{
	private static String TIEMPO_DE_BUSQUEDA = "Tiempo de com.iaic.searchs (ns)";
	private static String PATH_COST = "pathCost";
	
	private SearchOutcome outcome = SearchOutcome.FAILURE;

	private Object lastState = null;
	
	private ConditionTest conditionTest;
	long initialTime;
	
	public MaximumSlopeClimbingSearch(ConditionTest conditionTest) {
		super( );
		this.conditionTest = conditionTest;
	}
	
	public List search(Problem p) throws Exception {
		clearInstrumentation();
		outcome = SearchOutcome.FAILURE;
		lastState = null;
		Node current = new Node(p.getInitialState());
		Node neighbor = null;

		iniciarTiempoDeBusqueda();
		while (conditionTest==null || conditionTest.isConditionPassed(current)){
			List children = expandNode(current, p);
			neighbor = getHighestValuedNodeFrom(children, p);
			if (neighbor == null 
				|| (getValue(p, neighbor) <= getValue(p, current))) {
				if (p.isGoalState(current.getState())) {
					outcome = SearchOutcome.SOLUTION_FOUND;
				}
				lastState = current.getState();
				setPathCost(current.getPathCost());
				finishSearchTime();
				return SearchUtils.actionsFromNodes(current.getPathFromRoot());
			} else {
				current = neighbor;
			}
		}
		setPathCost(current.getPathCost());
		lastState = current.getState();
		finishSearchTime();
		return SearchUtils.actionsFromNodes(current.getPathFromRoot());
	}
	
	public SearchOutcome getOutcome() {
		return outcome;
	}

	public Object getLastSearchState() {
		return lastState;
	}
	
	private Node getHighestValuedNodeFrom(List children, Problem p) {
		double highestValue = Double.NEGATIVE_INFINITY;
		Node nodeWithHighestValue = null;
		for (int i = 0; i < children.size(); i++) {
			Node child = (Node) children.get(i);
			double value = getValue(p, child);
			if (value > highestValue) {
				highestValue = value;
				nodeWithHighestValue = child;
			}
		}
		return nodeWithHighestValue;
	}
	
	private double getValue(Problem p, Node n) {
		return -1 * getHeuristic(p, n); 
		
	}

	private double getHeuristic(Problem p, Node aNode) {
		return p.getHeuristicFunction().getHeuristicValue(aNode.getState());
	}
	
	private void finishSearchTime() {
		long tiempoFinal=System.nanoTime();
		metrics.set(TIEMPO_DE_BUSQUEDA,tiempoFinal-initialTime);
	}

	private void iniciarTiempoDeBusqueda() {
		initialTime=System.nanoTime();
	}
	
	public double getPathCost() {
		return metrics.getDouble(PATH_COST);
	}

	public void setPathCost(double pathCost) {
		metrics.set(PATH_COST, pathCost);
	}

}
